﻿using System;
using System.Globalization;
using System.Net;
using System.Threading;
using System.Threading.RateLimiting;
using System.Threading.Tasks;
using Common;
using Common.DataModels;
using Extensions.Services.RateLimit.Config;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.RateLimiting;

namespace Extensions.Services.RateLimit.Policys;
/// <summary>
/// 频率限制的基类
/// </summary>
public abstract class BaseLimiterPolicy:IRateLimiterPolicy<IPAddress>
{
    // 模版方法  让子类去写具体的实现
    protected RateLimitConfig Config => Appsettings.app<RateLimitConfig>("RateLimit");
    private IPAddress _remoteIpAddress;
    /// <summary>
    /// 子类实现的具体频率限制
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public RateLimitPartition<IPAddress> GetPartition(HttpContext httpContext)
    {
        ArgumentNullException.ThrowIfNull(Config);
        // 获取请求放的id
        _remoteIpAddress = httpContext.Connection.RemoteIpAddress!;
        // 如果不是回环地址  那么就根据ip来限流
        if (!IPAddress.IsLoopback(_remoteIpAddress))
        {
            return RateLimit(_remoteIpAddress);
        }
        return RateLimitPartition.GetNoLimiter(IPAddress.Loopback);
    }
    /// <summary>
    /// 具体的限流算法
    /// </summary>
    /// <param name="ipAddress"></param>
    /// <returns></returns>
    protected abstract RateLimitPartition<IPAddress> RateLimit(IPAddress ipAddress);
    /// <summary>
    /// 拒绝的返回
    /// </summary>
    public Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected => (context, cancellationToken) =>
    {
        // 获取下次可以尝试的时间
        if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
        {
            context.HttpContext.Response.Headers.RetryAfter =
                ((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
        }

        context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
        context.HttpContext.Response.WriteAsJsonAsync(new MessageModel()
        {
            code = 429,
            msg = "请求太过频繁,请稍后再尝试",
            success = false
        });
        return new ValueTask();
    };
}